package com.jht.bletool2java.service;

import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import com.jht.bletool2java.BLEApplication;
import com.jht.bletool2java.bean.BluetoothUUID;
import com.jht.bletool2java.util.CheckUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BLEService extends Service {
    //intent放置数据的参数键名，供广播用
    public final static String EXTRA_DATA_MTU_SIZE = "com.jht.bluetooth.le.EXTRA_DATA_MTU_SIZE";
    public final static String EXTRA_DATA_CURRENT_CHARACTERISTIC_NAME = "com.jht.bluetooth.le.EXTRA_DATA_CURRENT_CHARACTERISTIC_NAME";
    public final static String EXTRA_DATA_CURRENT_CHARACTERISTIC_DATA = "com.jht.bluetooth.le.EXTRA_DATA_CURRENT_CHARACTERISTIC_DATA";
    //intent Action，供广播用
    public final static String ACTION_GATT_CONNECTED = "com.jht.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED = "com.jht.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.jht.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_READ_DATA_OK = "com.jht.bluetooth.le.ACTION_READ_DATA_OK";
    public final static String ACTION_WRITE_DATA_OK = "com.jht.bluetooth.le.ACTION_WRITE_DATA_OK";
    public final static String ACTION_NOTIFY_DATA = "com.jht.bluetooth.le.ACTION_NOTIFY_DATA";
    public final static String ACTION_MTU_CHANGE = "com.jht.bluetooth.le.ACTION_MTU_CHANGE";

    private static final String TAG = "BLEService";
    //服务连接状态
    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;
    private final IBinder binder = new BleServiceBinder();
    //当前服务的连接状态
    private volatile int connectionState = STATE_DISCONNECTED;
    private BluetoothGatt bluetoothGatt;
    private BluetoothDevice currentBluetoothDevice;
    private volatile List<BluetoothGattService> allServices;
    private volatile List<BluetoothGattCharacteristic> allCharacteristics;
    private volatile Map<String, List<BluetoothGattCharacteristic>> map;

    private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                connectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                //发现服务
                bluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                connectionState = STATE_DISCONNECTED;
                bluetoothGatt.close();
                bluetoothGatt = null;
                broadcastUpdate(intentAction);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                allServices = gatt.getServices();
                if (allServices != null) {
                    allCharacteristics = new ArrayList<>();
                    map = new HashMap<>();
                    for (BluetoothGattService service : allServices) {
                        List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
                        if (characteristics != null) {
                            allCharacteristics.addAll(characteristics);
                        }
                        map.put(service.getUuid().toString(), characteristics);
                    }
                }
                //至此所有服务和特征的数据已经整合完毕，可以供使用
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered: There was a problem discovering services. status = " + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdateContainData(ACTION_READ_DATA_OK, characteristic);
            } else {
                Log.w(TAG, "onCharacteristicRead: There was a problem reading the data. status = " + status);
            }

        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdateNoData(characteristic);
            } else {
                Log.w(TAG, "onCharacteristicWrite: There was a problem writing the data. status = " + status);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            broadcastUpdateContainData(ACTION_NOTIFY_DATA, characteristic);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            Log.d(TAG, "onDescriptorRead: " + descriptor + "; status: " + status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            Log.d(TAG, "onDescriptorRead: " + descriptor + "; status: " + status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdateContainMtuData(mtu);
            } else {
                Log.w(TAG, "onMtuChanged: There was a problem changing Mtu.  status = " + status);
            }
        }

    };

    public BLEService() {
    }

    private void broadcastUpdate(final String action) {
        String info = "action = null";
        CheckUtil.isNoNull(info, action);

        Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    private void broadcastUpdateContainData(final String action, BluetoothGattCharacteristic characteristic) {
        String info = "action = " + action + " and characteristic = " + characteristic;
        CheckUtil.isNoNull(info, action, characteristic);

        Intent intent = new Intent(action);
        byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            StringBuilder stringBuilder = new StringBuilder(data.length);
            for (byte byteChar : data) {
                stringBuilder.append(String.format("%02X ", byteChar));
            }
            Log.d(TAG, "broadcastUpdateContainData: data => " + stringBuilder.toString());
            intent.putExtra(EXTRA_DATA_CURRENT_CHARACTERISTIC_NAME, characteristic.getUuid().toString());
            intent.putExtra(EXTRA_DATA_CURRENT_CHARACTERISTIC_DATA, data);
        }
        sendBroadcast(intent);
    }

    private void broadcastUpdateContainMtuData(int mtu) {
        Intent intent = new Intent(ACTION_MTU_CHANGE);
        intent.putExtra(EXTRA_DATA_MTU_SIZE, mtu);
        sendBroadcast(intent);
    }

    private void broadcastUpdateNoData(BluetoothGattCharacteristic characteristic) {
        String info = "broadcastUpdateNoData: action = " + ACTION_WRITE_DATA_OK + " and characteristic = " + characteristic;
        CheckUtil.isNoNull(info, ACTION_WRITE_DATA_OK, characteristic);

        Intent intent = new Intent(ACTION_WRITE_DATA_OK);
        intent.putExtra(EXTRA_DATA_CURRENT_CHARACTERISTIC_NAME, characteristic.getUuid().toString());
        sendBroadcast(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (bluetoothGatt == null) {
            return;
        }
        //如果此时 bluetoothGatt!=null，说明是直接关闭服务的，所以直接如下调用
        bluetoothGatt.disconnect();
        bluetoothGatt.close();
        bluetoothGatt=null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    //==============================================================================================
    /*
      以下的方法大部分都是供蓝牙通信操作的方法
     */
    //==============================================================================================

    /**
     * @param bluetoothDevice 需要被连接的设备
     */
    public void connectGatt(BluetoothDevice bluetoothDevice) {
        if (bluetoothDevice == null) {
            Log.w(TAG, "connectGatt: fail! bluetoothDevice == null");
            return;
        }
        currentBluetoothDevice = bluetoothDevice;
        bluetoothGatt = bluetoothDevice.connectGatt(BLEApplication.getBLEApplicationContext(), false, gattCallback);
        connectionState = STATE_CONNECTING;
    }

    public void disConnected() {
        if (bluetoothGatt == null) {
            return;
        }
        bluetoothGatt.disconnect();
    }

    public boolean reConnected() {
        if(connectionState == STATE_CONNECTED){
            Log.w(TAG, "reConnected: currentBluetoothDevice connectionState = STATE_CONNECTED!");
            return true;
        }
        if (bluetoothGatt == null) {
            if (currentBluetoothDevice == null) {
                Log.w(TAG, "reConnected: currentBluetoothDevice == null!");
                return false;
            }
            Log.d(TAG, "reConnected: reConnected new!");
            bluetoothGatt = currentBluetoothDevice.connectGatt(BLEApplication.getBLEApplicationContext(), false, gattCallback);
            return true;
        }
        Log.d(TAG, "reConnected: reConnected old!");
        return bluetoothGatt.connect();
    }

    public int connectStatus() {
        return connectionState;
    }

    public List<BluetoothGattService> getAllServices(){
        return allServices;
    }

    public List<BluetoothGattCharacteristic> getAllCharacteristics(){
        return allCharacteristics;
    }

    public Map<String, List<BluetoothGattCharacteristic>> getServicesCharacteristicsMap(){
        return map;
    }


    public boolean readGatt(BluetoothGattCharacteristic bluetoothGattCharacteristic){
        boolean showTip = CheckUtil.isNoNullShowTip("readGatt: Gatt or characteristic is null", bluetoothGatt, bluetoothGattCharacteristic);
        if (!showTip) {
            //显示Toast
            return false;
        }
        return bluetoothGatt.readCharacteristic(bluetoothGattCharacteristic);
    }

    public boolean writeGatt(BluetoothGattCharacteristic bluetoothGattCharacteristic,byte[] data){
        boolean showTip = CheckUtil.isNoNullShowTip("readGatt: Gatt ,data or characteristic is null", bluetoothGatt, bluetoothGattCharacteristic,data);
        if (!showTip) {
            //显示Toast
            return false;
        }
        if (data.length > 20){
            return false;
        }
        bluetoothGattCharacteristic.setValue(data);
        return bluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
    }



    /**
     * @param uuid 例如："00002ad9-0000-1000-8000-00805f9b34fb" 这样的形式
     * @return 返回当前服务连接的蓝牙设备的特征（根据uuid），没有的话返回null
     */
    public BluetoothGattCharacteristic getCurrentDeviceGattCharacteristic(String uuid) {
        boolean showTip = CheckUtil.isNoNullShowTip("getCurrentDeviceGattCharacteristic: uuid is null", uuid);
        boolean showTip1 = CheckUtil.isNoNullShowTip("getCurrentDeviceGattCharacteristic: allCharacteristics is null", allCharacteristics);
        if (!showTip || !showTip1) {
            //显示Toast
            return null;
        }
        for (BluetoothGattCharacteristic gattCharacteristic : allCharacteristics) {
            if (gattCharacteristic.getUuid().toString().equals(uuid)) {
                return gattCharacteristic;
            }
        }
        return null;
    }

    /**
     * @param characteristic 需要设置的特征
     * @return false说明开启通知失败，true为成功
     */
    public boolean enableNotificationOrIndication(BluetoothGattCharacteristic characteristic) {
        boolean showTip = CheckUtil.isNoNullShowTip("enableNotification: characteristic == null ", characteristic);
        if (!showTip) {
            //显示Toast
            return false;
        }
        if (characteristic.getProperties() == BluetoothGattCharacteristic.PROPERTY_NOTIFY) { // Configuration for receive notifications from device
            return enableNotification(true, characteristic);
        } else if (characteristic.getProperties() == BluetoothGattCharacteristic.PROPERTY_INDICATE) { // Configuration for indicate
            return enableIndication(true, characteristic);
        }
        return false;
    }

    /**
     * @param characteristic 需要设置的特征
     * @return false说明关闭通知失败，true为成功
     */
    public boolean disEnableNotificationOrIndication(BluetoothGattCharacteristic characteristic) {
        boolean showTip = CheckUtil.isNoNullShowTip("enableNotification: characteristic == null ", characteristic);
        if (!showTip) {
            //显示Toast
            return false;
        }
        if (characteristic.getProperties() == BluetoothGattCharacteristic.PROPERTY_NOTIFY) { // Configuration for receive notifications from device
            return enableNotification(false, characteristic);
        } else if (characteristic.getProperties() == BluetoothGattCharacteristic.PROPERTY_INDICATE) { // Configuration for indicate
            return enableIndication(false, characteristic);
        }
        return false;
    }

    private boolean enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) {
        boolean showTip = CheckUtil.isNoNullShowTip("enableNotification: Gatt or characteristic is null", bluetoothGatt, characteristic);
        if (!showTip) {
            //显示Toast
            return false;
        }
        if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
            Log.w(TAG, "enableNotification: setCharacteristicNotification is fail!");
            return false;
        }

        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(BluetoothUUID.CLIENT_CONFIG_UUID);
        if (clientConfig == null) {
            Log.w(TAG, "enableNotification: BluetoothGattDescriptor is null!");
            return false;
        }

        if (enable) {
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return bluetoothGatt.writeDescriptor(clientConfig);
    }

    private boolean enableIndication(boolean enable, BluetoothGattCharacteristic characteristic) {
        if (bluetoothGatt == null) return false;
        if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
            Log.w(TAG, "enableIndication: setCharacteristicNotification is fail!");
            return false;
        }

        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(BluetoothUUID.CLIENT_CONFIG_UUID);
        if (clientConfig == null) {
            Log.w(TAG, "enableIndication: BluetoothGattDescriptor is null!");
            return false;
        }

        if (enable) {
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
        } else {
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return bluetoothGatt.writeDescriptor(clientConfig);
    }

    public class BleServiceBinder extends Binder {

        public BLEService getService() {
            return BLEService.this;
        }

    }

}
